/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.editor.codecompletion.revisited;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.python.pydev.core.FullRepIterable;
import org.python.pydev.core.ICodeCompletionASTManager;
import org.python.pydev.core.ICompletionRequest;
import org.python.pydev.core.ICompletionState;
import org.python.pydev.core.IGrammarVersionProvider;
import org.python.pydev.core.ILocalScope;
import org.python.pydev.core.IModule;
import org.python.pydev.core.IModulesManager;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.IToken;
import org.python.pydev.core.ImmutableTuple;
import org.python.pydev.core.MisconfigurationException;
import org.python.pydev.core.ModulesKey;
import org.python.pydev.core.Tuple3;
import org.python.pydev.core.TupleN;
import org.python.pydev.core.callbacks.ICallback0;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.core.log.Log;
import org.python.pydev.core.structure.CompletionRecursionException;
import org.python.pydev.editor.actions.PyAction;
import org.python.pydev.editor.codecompletion.revisited.modules.AbstractModule;
import org.python.pydev.editor.codecompletion.revisited.modules.SourceModule;
import org.python.pydev.editor.codecompletion.revisited.modules.SourceToken;
import org.python.pydev.editor.codecompletion.revisited.visitors.AbstractVisitor;
import org.python.pydev.editor.codecompletion.revisited.visitors.Definition;
import org.python.pydev.editor.codecompletion.revisited.visitors.GlobalModelVisitor;
import org.python.pydev.logging.DebugSettings;
import org.python.pydev.parser.PyParser;
import org.python.pydev.parser.jython.SimpleNode;
import org.python.pydev.parser.jython.ast.Import;
import org.python.pydev.parser.jython.ast.ImportFrom;
import org.python.pydev.parser.jython.ast.NameTok;
import org.python.pydev.parser.jython.ast.aliasType;
import org.python.pydev.parser.visitors.NodeUtils;
import com.aptana.shared_core.io.FileUtils;
import com.aptana.shared_core.structure.Tuple;
public abstract class AbstractASTManager implements ICodeCompletionASTManager {
private static final IToken[] EMPTY_ITOKEN_ARRAY = new IToken[0];
private static final boolean DEBUG_CACHE = false;
private final AssignAnalysis assignAnalysis = new AssignAnalysis();
public AbstractASTManager() {
}
private final Object lock = new Object();
public Object getLock() {
return lock;
}
/**
* This is the guy that will handle project things for us
*/
public volatile IModulesManager modulesManager;
public IModulesManager getModulesManager() {
return modulesManager;
}
/**
* Set the nature this ast manager works with (if no project is available and a nature is).
*/
public void setNature(IPythonNature nature) {
getModulesManager().setPythonNature(nature);
}
public IPythonNature getNature() {
return getModulesManager().getNature();
}
public abstract void setProject(IProject project, IPythonNature nature, boolean restoreDeltas);
public abstract void rebuildModule(File file, ICallback0<IDocument> doc, IProject project,
IProgressMonitor monitor, IPythonNature nature);
public abstract void removeModule(File file, IProject project, IProgressMonitor monitor);
/**
* Returns the imports that start with a given string. The comparison is not case dependent. Passes all the modules in the cache.
*
* @param original is the name of the import module eg. 'from toimport import ' would mean that the original is 'toimport'
* or something like 'foo.bar' or an empty string (if only 'import').
* @return a Set with the imports as tuples with the name, the docstring.
* @throws CompletionRecursionException
* @throws MisconfigurationException
*/
public IToken[] getCompletionsForImport(ImportInfo importInfo, ICompletionRequest r, boolean onlyGetDirectModules)
throws CompletionRecursionException, MisconfigurationException {
String original = importInfo.importsTipperStr;
String afterDots = null;
int level = 0; //meaning: no absolute import
boolean onlyDots = true;
if (original.startsWith(".")) {
//if the import has leading dots, this means it is something like
//from ...bar import xxx (new way to express the relative import)
for (int i = 0; i < original.length(); i++) {
if (original.charAt(i) != '.') {
onlyDots = false;
afterDots = original.substring(i);
break;
}
//add one to the relative import level
level++;
}
}
ICompletionRequest request = r;
IPythonNature nature = request.getNature();
String relative = null;
String moduleName = null;
if (request.getEditorFile() != null) {
moduleName = modulesManager.resolveModule(FileUtils.getFileAbsolutePath(request.getEditorFile()));
if (moduleName != null) {
if (level > 0) {
//ok, it is the import added on python 2.5 (from .. import xxx)
List<String> moduleParts = StringUtils.dotSplit(moduleName);
if (moduleParts.size() > level) {
relative = FullRepIterable.joinParts(moduleParts, moduleParts.size() - level);
}
if (!onlyDots) {
//ok, we have to add the other part too, as we have more than the leading dots
//from ..bar import
relative += "." + afterDots;
}
} else {
boolean isAbsoluteImportEnabled = isAbsoluteImportEnabled(request, nature);
if (!isAbsoluteImportEnabled) {
String tail = FullRepIterable.headAndTail(moduleName)[0];
if (original.length() > 0) {
relative = tail + "." + original;
} else {
relative = tail;
}
}
}
}
}
//set to hold the completion (no duplicates allowed).
Set<IToken> set = new HashSet<IToken>();
String absoluteModule = original;
if (absoluteModule.endsWith(".")) {
absoluteModule = absoluteModule.substring(0, absoluteModule.length() - 1); //remove last char
}
//If we have a relative import, first match with the relative and only try to match the absolute if the relative
//was not found.
if (relative != null && relative.equals(absoluteModule) == false) {
getAbsoluteImportTokens(relative, set, IToken.TYPE_RELATIVE_IMPORT, false, importInfo, onlyGetDirectModules);
if (importInfo.hasImportSubstring) {
getTokensForModule(relative, nature, relative, set);
}
}
if (set.size() == 0 || absoluteModule.length() == 0 //In the case of an "import zi", the absoluteModule will be an empty string, and we have to get the roots in the completion!
) {
if (level == 0) {
//first we get the imports... that complete for the token.
getAbsoluteImportTokens(absoluteModule, set, IToken.TYPE_IMPORT, false, importInfo,
onlyGetDirectModules);
//Now, if we have an initial module, we have to get the completions
//for it.
getTokensForModule(original, nature, absoluteModule, set);
}
}
if (level == 1 && moduleName != null) {
//has returned itself, so, let's remove it
String strToRemove = FullRepIterable.getLastPart(moduleName);
for (Iterator<IToken> it = set.iterator(); it.hasNext();) {
IToken o = it.next();
if (o.getRepresentation().equals(strToRemove)) {
it.remove();
//don't break because the token might be different, but not the representation...
}
}
}
return set.toArray(EMPTY_ITOKEN_ARRAY);
}
private boolean isAbsoluteImportEnabled(IModule module, IPythonNature nature) throws MisconfigurationException {
boolean isAbsoluteImportEnabled = false;
try {
isAbsoluteImportEnabled = nature.getGrammarVersion() >= IGrammarVersionProvider.GRAMMAR_PYTHON_VERSION_3_0;
} catch (MisconfigurationException e) {
Log.log(e);
}
if (!isAbsoluteImportEnabled) {
//Let's check in Python 2.x if the from __future__ import absolute_import is there.
if (module != null) {
isAbsoluteImportEnabled = module.hasFutureImportAbsoluteImportDeclared();
}
}
return isAbsoluteImportEnabled;
}
private boolean isAbsoluteImportEnabled(ICompletionRequest request, IPythonNature nature)
throws MisconfigurationException {
boolean isAbsoluteImportEnabled = false;
try {
isAbsoluteImportEnabled = nature.getGrammarVersion() >= IGrammarVersionProvider.GRAMMAR_PYTHON_VERSION_3_0;
} catch (MisconfigurationException e) {
Log.log(e);
}
if (!isAbsoluteImportEnabled) {
//Let's check in Python 2.x if the from __future__ import absolute_import is there.
IModule module = request.getModule();
if (module != null) {
isAbsoluteImportEnabled = module.hasFutureImportAbsoluteImportDeclared();
}
}
return isAbsoluteImportEnabled;
}
/**
* @param moduleToGetTokensFrom the string that represents the token from where we are getting the imports
* @param set the set where the tokens should be added
* @param importInfo if null, only the 1st element of the module will be added, otherwise, it'll check the info
* to see if it should add only the 1st element of the module or the complete module (e.g.: add only xml or
* xml.dom and other submodules too)
*/
public void getAbsoluteImportTokens(String moduleToGetTokensFrom, Set<IToken> inputOutput, int type,
boolean onlyFilesOnSameLevel, ImportInfo importInfo, boolean onlyGetDirectModules) {
// boolean getSubModules = false;
// if(importInfo != null){
// //we only want to get submodules if we're in:
// //from xxx
// //import xxx
// //
// //We do NOT want to get it on:
// //from xxx import yyy
// if(importInfo.hasFromSubstring != importInfo.hasImportSubstring){
// getSubModules = true;
// }
// }
HashMap<String, IToken> temp = new HashMap<String, IToken>();
SortedMap<ModulesKey, ModulesKey> modulesStartingWith;
if (onlyGetDirectModules) {
modulesStartingWith = modulesManager.getAllDirectModulesStartingWith(moduleToGetTokensFrom);
} else {
modulesStartingWith = modulesManager.getAllModulesStartingWith(moduleToGetTokensFrom);
}
Iterator<ModulesKey> itModules = modulesStartingWith.keySet().iterator();
while (itModules.hasNext()) {
ModulesKey key = itModules.next();
String element = key.name;
// if (element.startsWith(moduleToGetTokensFrom)) { we don't check that anymore because we get all the modules starting with it already
if (onlyFilesOnSameLevel && key.file != null && key.file.isDirectory()) {
continue; // we only want those that are in the same directory, and not in other directories...
}
element = element.substring(moduleToGetTokensFrom.length());
//we just want those that are direct
//this means that if we had initially element = testlib.unittest.anothertest
//and element became later = .unittest.anothertest, it will be ignored (we
//should only analyze it if it was something as testlib.unittest and became .unittest
//we only check this if we only want file modules (in
if (onlyFilesOnSameLevel && PyAction.countChars('.', element) > 1) {
continue;
}
boolean goForIt = false;
//if initial is not empty only get those that start with a dot (submodules, not
//modules that start with the same name).
//e.g. we want xml.dom
//and not xmlrpclib
//if we have xml token (not using the qualifier here)
if (moduleToGetTokensFrom.length() != 0) {
if (element.length() > 0 && element.charAt(0) == ('.')) {
element = element.substring(1);
goForIt = true;
}
} else {
goForIt = true;
}
if (element.length() > 0 && goForIt) {
List<String> splitted = StringUtils.dotSplit(element);
if (splitted.size() > 0) {
String strToAdd;
strToAdd = splitted.get(0);
// if(!getSubModules){
// }else{
// if(element.endsWith(".__init__")){
// strToAdd = element.substring(0, element.length()-9);
// }else{
// strToAdd = element;
// }
// }
//this is the completion
temp.put(strToAdd, new ConcreteToken(strToAdd, "", "", moduleToGetTokensFrom, type));
}
}
// }
}
inputOutput.addAll(temp.values());
}
/**
* @param original this is the initial module where the completion should happen (may have class in it too)
* @param moduleToGetTokensFrom
* @param set set where the tokens should be added
* @throws CompletionRecursionException
*/
protected void getTokensForModule(String original, IPythonNature nature, String moduleToGetTokensFrom,
Set<IToken> set) throws CompletionRecursionException {
if (moduleToGetTokensFrom.length() > 0) {
if (original.endsWith(".")) {
original = original.substring(0, original.length() - 1);
}
Tuple<IModule, String> modTok = findModuleFromPath(original, nature, false, null); //the current module name is not used as it is not relative
IModule m = modTok.o1;
String tok = modTok.o2;
if (m == null) {
//we were unable to find it with the given path, so, there's nothing else to do here...
return;
}
IToken[] globalTokens;
if (tok != null && tok.length() > 0) {
CompletionState state2 = new CompletionState(-1, -1, tok, nature, "");
state2.setBuiltinsGotten(true); //we don't want to get builtins here
globalTokens = m.getGlobalTokens(state2, this);
} else {
CompletionState state2 = new CompletionState(-1, -1, "", nature, "");
state2.setBuiltinsGotten(true); //we don't want to get builtins here
globalTokens = getCompletionsForModule(m, state2);
}
for (int i = 0; i < globalTokens.length; i++) {
IToken element = globalTokens[i];
//this is the completion
set.add(element);
}
}
}
/**
* @param file
* @param doc
* @param state
* @return
* @throws MisconfigurationException
*/
public static IModule createModule(File file, IDocument doc, IPythonNature nature) throws MisconfigurationException {
return AbstractModule.createModuleFromDoc(file, doc, nature);
}
// /**
// * @throws MisconfigurationException
// * @see org.python.pydev.core.ICodeCompletionASTManager#getCompletionsForToken(java.io.File, org.eclipse.jface.text.IDocument, org.python.pydev.editor.codecompletion.revisited.CompletionState)
// */
// public IToken[] getCompletionsForToken(File file, IDocument doc, ICompletionState state) throws CompletionRecursionException, MisconfigurationException {
// IModule module = createModule(file, doc, state, this);
// return getCompletionsForModule(module, state, true, true);
// }
/**
* @see org.python.pydev.editor.codecompletion.revisited.ICodeCompletionASTManage#getCompletionsForToken(org.eclipse.jface.text.IDocument, org.python.pydev.editor.codecompletion.revisited.CompletionState)
*/
public IToken[] getCompletionsForToken(IDocument doc, ICompletionState state) {
IToken[] completionsForModule;
try {
Tuple<SimpleNode, Throwable> obj = PyParser
.reparseDocument(new PyParser.ParserInfo(doc, state.getNature()));
SimpleNode n = obj.o1;
IModule module = AbstractModule.createModule(n);
completionsForModule = getCompletionsForModule(module, state, true, true);
} catch (Exception e) {
String message = e.getMessage();
if (message == null) {
if (e instanceof NullPointerException) {
Log.log(e);
message = "NullPointerException";
} else {
message = "Null error message";
}
}
completionsForModule = new IToken[] { new ConcreteToken(message, message, "", "", IToken.TYPE_UNKNOWN) };
}
return completionsForModule;
}
/**
* By default does not look for relative import
*/
public IModule getModule(String name, IPythonNature nature, boolean dontSearchInit) {
return modulesManager.getModule(name, nature, dontSearchInit, false);
}
/**
* This method returns the module that corresponds to the path passed as a parameter.
*
* @param name the name of the module we're looking for
* @param lookingForRelative determines whether we're looking for a relative module (in which case we should
* not check in other places... only in the module)
* @return the module represented by this name
*/
public IModule getModule(String name, IPythonNature nature, boolean dontSearchInit, boolean lookingForRelative) {
if (lookingForRelative) {
return modulesManager.getRelativeModule(name, nature);
} else {
return modulesManager.getModule(name, nature, dontSearchInit);
}
}
/**
* Identifies the token passed and if it maps to a builtin not 'easily recognizable', as
* a string or list, we return it.
*
* @param state
* @return
*/
protected IToken[] getBuiltinsCompletions(ICompletionState state) {
ICompletionState state2 = state.getCopy();
String act = state.getActivationToken();
//check for the builtin types.
state2.setActivationToken(NodeUtils.getBuiltinType(act));
if (state2.getActivationToken() != null) {
IModule m = getBuiltinMod(state.getNature());
if (m != null) {
return m.getGlobalTokens(state2, this);
}
}
if (act.equals("__builtins__") || act.startsWith("__builtins__.")) {
act = act.substring(12);
if (act.startsWith(".")) {
act = act.substring(1);
}
IModule m = getBuiltinMod(state.getNature());
ICompletionState state3 = state.getCopy();
state3.setActivationToken(act);
return m.getGlobalTokens(state3, this);
}
return null;
}
/**
* @throws CompletionRecursionException
* @see org.python.pydev.editor.codecompletion.revisited.ICodeCompletionASTManage#getCompletionsForModule(org.python.pydev.editor.codecompletion.revisited.modules.AbstractModule, org.python.pydev.editor.codecompletion.revisited.CompletionState)
*/
public IToken[] getCompletionsForModule(IModule module, ICompletionState state) throws CompletionRecursionException {
return getCompletionsForModule(module, state, true);
}
/**
* @throws CompletionRecursionException
* @see org.python.pydev.editor.codecompletion.revisited.ICodeCompletionASTManage#getCompletionsForModule(org.python.pydev.editor.codecompletion.revisited.modules.AbstractModule, org.python.pydev.editor.codecompletion.revisited.CompletionState, boolean)
*/
public IToken[] getCompletionsForModule(IModule module, ICompletionState state, boolean searchSameLevelMods)
throws CompletionRecursionException {
return getCompletionsForModule(module, state, true, false);
}
/**
* @see org.python.pydev.editor.codecompletion.revisited.ICodeCompletionASTManage#getCompletionsForModule(org.python.pydev.editor.codecompletion.revisited.modules.AbstractModule, org.python.pydev.editor.codecompletion.revisited.CompletionState, boolean, boolean)
*/
public IToken[] getCompletionsForModule(IModule module, ICompletionState state, boolean searchSameLevelMods,
boolean lookForArgumentCompletion) throws CompletionRecursionException {
return getCompletionsForModule(module, state, searchSameLevelMods, lookForArgumentCompletion, false);
}
/**
* @see #getCompletionsForModule(IModule, ICompletionState, boolean, boolean)
*
* Same thing but may handle things as if it was a wild import (in which case, the tokens starting with '_' are
* removed and if __all__ is available, only the tokens contained in __all__ are returned)
*/
public IToken[] getCompletionsForModule(IModule module, ICompletionState state, boolean searchSameLevelMods,
boolean lookForArgumentCompletion, boolean handleAsWildImport) throws CompletionRecursionException {
String name = module.getName();
Object key = new TupleN("getCompletionsForModule", name != null ? name : "", state.getActivationToken(),
searchSameLevelMods, lookForArgumentCompletion, state.getBuiltinsGotten(),
state.getLocalImportsGotten(), handleAsWildImport);
IToken[] ret = (IToken[]) state.getObj(key);
if (ret != null) {
if (DEBUG_CACHE) {
System.out.println("Checking if cache is correct for: " + key);
IToken[] internal = internalGenerateGetCompletionsForModule(module, state, searchSameLevelMods,
lookForArgumentCompletion);
internal = filterForWildImport(module, handleAsWildImport, internal);
//the new request may actually have no tokens if a completion exception occurred.
if (internal.length != 0 && ret.length != internal.length) {
throw new RuntimeException("This can't happen... it should always return the same completions!");
}
}
return ret;
}
IToken[] completionsForModule = internalGenerateGetCompletionsForModule(module, state, searchSameLevelMods,
lookForArgumentCompletion);
completionsForModule = filterForWildImport(module, handleAsWildImport, completionsForModule);
state.add(key, completionsForModule);
return completionsForModule;
}
/**
* Filters the tokens according to the wild import rules:
* - the tokens starting with '_' are removed
* - if __all__ is available, only the tokens contained in __all__ are returned)
*/
private IToken[] filterForWildImport(IModule module, boolean handleAsWildImport, IToken[] completionsForModule) {
if (module != null && handleAsWildImport) {
ArrayList<IToken> ret = new ArrayList<IToken>();
for (int j = 0; j < completionsForModule.length; j++) {
IToken token = completionsForModule[j];
//on wild imports we don't get names that start with '_'
if (!token.getRepresentation().startsWith("_")) {
ret.add(token);
}
}
if (module instanceof SourceModule) {
//Support for __all__: filter things if __all__ is available.
SourceModule sourceModule = (SourceModule) module;
GlobalModelVisitor globalModelVisitorCache = sourceModule.getGlobalModelVisitorCache();
if (globalModelVisitorCache != null) {
globalModelVisitorCache.filterAll(ret);
}
}
return ret.toArray(new IToken[ret.size()]);
} else {
return completionsForModule;
}
}
private void log(String message, IModule module, ICompletionState state) {
String name;
if (module == null) {
name = "null module";
} else {
name = module.getName();
}
Log.toLogFile(this, message + ": " + name + " -- " + state.getActivationToken());
}
/**
* This method should only be accessed from the public getCompletionsForModule (which caches the result).
*/
private IToken[] internalGenerateGetCompletionsForModule(IModule module, ICompletionState state,
boolean searchSameLevelMods, boolean lookForArgumentCompletion) throws CompletionRecursionException {
if (DebugSettings.DEBUG_CODE_COMPLETION) {
log("internalGenerateGetCompletionsForModule", module, state);
}
ArrayList<IToken> importedModules = new ArrayList<IToken>();
ILocalScope localScope = null;
int line = state.getLine();
int col = state.getCol();
if (state.getLocalImportsGotten() == false) {
//in the first analyzed module, we have to get the local imports too.
state.setLocalImportsGotten(true);
if (module != null && line >= 0) {
localScope = module.getLocalScope(line, col);
if (localScope != null) {
importedModules.addAll(localScope.getLocalImportedModules(line + 1, col + 1, module.getName()));
}
}
}
IToken[] builtinsCompletions = getBuiltinsCompletions(state);
if (builtinsCompletions != null) {
return builtinsCompletions;
}
String act = state.getActivationToken();
int parI = act.indexOf('(');
if (parI != -1) {
state.setFullActivationToken(act);
act = act.substring(0, parI);
state.setActivationToken(act);
state.setLookingFor(ICompletionState.LOOKING_FOR_INSTANCED_VARIABLE);
}
if (module != null) {
//get the tokens (global, imported and wild imported)
IToken[] globalTokens = module.getGlobalTokens();
List<IToken> tokenImportedModules = Arrays.asList(module.getTokenImportedModules());
importedModules.addAll(tokenImportedModules);
state.setTokenImportedModules(importedModules);
IToken[] wildImportedModules = module.getWildImportedModules();
//now, lets check if this is actually a module that is an __init__ (if so, we have to get all
//the other .py files as modules that are in the same level as the __init__)
Set<IToken> initial = new HashSet<IToken>();
if (searchSameLevelMods) {
//now, we have to ask for the module if it's a 'package' (folders that have __init__.py for python
//or only folders -- not classes -- in java).
if (module.isPackage()) {
HashSet<IToken> gotten = new HashSet<IToken>();
//the module also decides how to get its submodules
getAbsoluteImportTokens(module.getPackageFolderName(), gotten, IToken.TYPE_IMPORT, true, null,
false);
for (IToken token : gotten) {
if (token.getRepresentation().equals("__init__") == false) {
initial.add(token);
}
}
}
}
if (state.getActivationToken().length() == 0) {
List<IToken> completions = getGlobalCompletions(globalTokens,
importedModules.toArray(EMPTY_ITOKEN_ARRAY), wildImportedModules, state, module);
//now find the locals for the module
if (line >= 0) {
IToken[] localTokens = module.getLocalTokens(line, col, localScope);
for (int i = 0; i < localTokens.length; i++) {
completions.add(localTokens[i]);
}
}
completions.addAll(initial); //just add all that are in the same level if it was an __init__
return completions.toArray(EMPTY_ITOKEN_ARRAY);
} else { //ok, we have a token, find it and get its completions.
//first check if the token is a module... if it is, get the completions for that module.
IToken[] tokens = findTokensOnImportedMods(importedModules.toArray(EMPTY_ITOKEN_ARRAY), state, module);
if (tokens != null && tokens.length > 0) {
return decorateWithLocal(tokens, localScope, state);
}
//if it is an __init__, modules on the same level are treated as local tokens
if (searchSameLevelMods) {
tokens = searchOnSameLevelMods(initial, state);
if (tokens != null && tokens.length > 0) {
return decorateWithLocal(tokens, localScope, state);
}
}
//for wild imports, we must get the global completions with __all__ filtered
//wild imports: recursively go and get those completions and see if any matches it.
for (int i = 0; i < wildImportedModules.length; i++) {
IToken name = wildImportedModules[i];
IModule mod = getModule(name.getAsRelativeImport(module.getName()), state.getNature(), false); //relative (for wild imports this is ok... only a module can be used in wild imports)
if (mod == null) {
mod = getModule(name.getOriginalRep(), state.getNature(), false); //absolute
}
if (mod != null) {
state.checkFindModuleCompletionsMemory(mod, state.getActivationToken());
IToken[] completionsForModule = getCompletionsForModule(mod, state);
if (completionsForModule.length > 0)
return decorateWithLocal(completionsForModule, localScope, state);
} else {
//"Module not found:" + name.getRepresentation()
}
}
//it was not a module (would have returned already), so, try to get the completions for a global token defined.
tokens = module.getGlobalTokens(state, this);
if (tokens.length > 0) {
return decorateWithLocal(tokens, localScope, state);
}
//If it was still not found, go to builtins.
IModule builtinsMod = getBuiltinMod(state.getNature());
if (builtinsMod != null && builtinsMod != module) {
tokens = getCompletionsForModule(builtinsMod, state);
if (tokens.length > 0) {
if (tokens[0].getRepresentation().equals("ERROR:") == false) {
return decorateWithLocal(tokens, localScope, state);
}
}
}
if (lookForArgumentCompletion && localScope != null) {
//now, if we have to look for arguments and search things in the local scope, let's also
//check for assert (isinstance...) in this scope with the given variable.
List<String> lookForClass = localScope.getPossibleClassesForActivationToken(state
.getActivationToken());
if (lookForClass.size() > 0) {
HashSet<IToken> hashSet = new HashSet<IToken>();
getCompletionsForClassInLocalScope(module, state, searchSameLevelMods,
lookForArgumentCompletion, lookForClass, hashSet);
if (hashSet.size() > 0) {
return hashSet.toArray(EMPTY_ITOKEN_ARRAY);
}
}
//ok, didn't find in assert isinstance... keep going
//if there was no assert for the class, get from extensions / local scope interface
tokens = CompletionParticipantsHelper.getCompletionsForMethodParameter(state, localScope).toArray(
EMPTY_ITOKEN_ARRAY);
if (tokens != null && tokens.length > 0) {
return tokens;
}
}
//nothing worked so far, so, let's look for an assignment...
return getAssignCompletions(module, state, lookForArgumentCompletion, localScope);
}
} else {
Log.log("Module passed in is null!!");
}
return EMPTY_ITOKEN_ARRAY;
}
private IToken[] decorateWithLocal(IToken[] tokens, ILocalScope localScope, ICompletionState state) {
if (localScope != null) {
Collection<IToken> interfaceForLocal = localScope.getInterfaceForLocal(state.getActivationToken());
if (interfaceForLocal != null && interfaceForLocal.size() > 0) {
IToken[] ret = new IToken[tokens.length + interfaceForLocal.size()];
Object[] array = interfaceForLocal.toArray();
System.arraycopy(array, 0, ret, 0, array.length);
System.arraycopy(tokens, 0, ret, array.length, tokens.length);
return ret;
}
}
return tokens;
}
private IToken[] getAssignCompletions(IModule module, ICompletionState state, boolean lookForArgumentCompletion,
ILocalScope localScope) {
AssignCompletionInfo assignCompletions = assignAnalysis.getAssignCompletions(this, module, state);
boolean useExtensions = assignCompletions.completions.size() == 0;
if (lookForArgumentCompletion && localScope != null && assignCompletions.completions.size() == 0
&& assignCompletions.defs.length > 0) {
//Now, if a definition found was available in the same scope we started on, let's add the
//tokens that are available from that scope.
for (Definition d : assignCompletions.defs) {
if (d.module.equals(module) && localScope.equals(d.scope)) {
Collection<IToken> interfaceForLocal = localScope.getInterfaceForLocal(state.getActivationToken());
assignCompletions.completions.addAll(interfaceForLocal);
break;
}
}
}
if (useExtensions && localScope != null) {
assignCompletions.completions.addAll(CompletionParticipantsHelper.getCompletionsForTokenWithUndefinedType(
state, localScope));
}
return assignCompletions.completions.toArray(EMPTY_ITOKEN_ARRAY);
}
/**
* @see ICodeCompletionASTManager#getCompletionsForClassInLocalScope(IModule, ICompletionState, boolean, boolean, List, HashSet)
*/
public void getCompletionsForClassInLocalScope(IModule module, ICompletionState state, boolean searchSameLevelMods,
boolean lookForArgumentCompletion, List<String> lookForClass, HashSet<IToken> hashSet)
throws CompletionRecursionException {
IToken[] tokens;
//if found here, it's an instanced variable (force it and restore if we didn't find it here...)
ICompletionState stateCopy = state.getCopy();
int prevLookingFor = stateCopy.getLookingFor();
//force looking for instance
stateCopy.setLookingFor(ICompletionState.LOOKING_FOR_INSTANCED_VARIABLE, true);
for (String classFound : lookForClass) {
stateCopy.setLocalImportsGotten(false);
stateCopy.setActivationToken(classFound);
//same thing as the initial request, but with the class we could find...
tokens = getCompletionsForModule(module, stateCopy, searchSameLevelMods, lookForArgumentCompletion);
if (tokens != null) {
for (IToken tok : tokens) {
hashSet.add(tok);
}
}
}
if (hashSet.size() == 0) {
//force looking for what was set before...
stateCopy.setLookingFor(prevLookingFor, true);
}
}
/**
* Attempt to search on modules on the same level as this one (this will only happen if we are in an __init__
* module (otherwise, the initial set will be empty)
*
* @param initial this is the set of tokens generated from modules in the same level
* @param state the current state of the completion
*
* @return a list of tokens found.
* @throws CompletionRecursionException
*/
protected IToken[] searchOnSameLevelMods(Set<IToken> initial, ICompletionState state)
throws CompletionRecursionException {
IToken[] ret = null;
Tuple<IModule, IModulesManager> modUsed = null;
String actTokUsed = null;
for (IToken token : initial) {
//ok, maybe it was from the set that is in the same level as this one (this will only happen if we are on an __init__ module)
String rep = token.getRepresentation();
if (state.getActivationToken().startsWith(rep)) {
String absoluteImport = token.getAsAbsoluteImport();
modUsed = modulesManager.getModuleAndRelatedModulesManager(absoluteImport, state.getNature(), true,
false);
IModule sameLevelMod = null;
if (modUsed != null) {
sameLevelMod = modUsed.o1;
}
if (sameLevelMod == null) {
return null;
}
String qualifier = state.getActivationToken().substring(rep.length());
if (state.getActivationToken().equals(rep)) {
actTokUsed = "";
} else if (qualifier.startsWith(".")) {
actTokUsed = qualifier.substring(1);
}
if (actTokUsed != null) {
ICompletionState copy = state.getCopyWithActTok(actTokUsed);
copy.setBuiltinsGotten(true); //we don't want builtins...
ret = getCompletionsForModule(sameLevelMod, copy);
break;
}
}
}
return ret;
}
/**
* @see ICodeCompletionASTManager#getGlobalCompletions
*/
public List<IToken> getGlobalCompletions(IToken[] globalTokens, IToken[] importedModules,
IToken[] wildImportedModules, ICompletionState state, IModule current) {
if (DebugSettings.DEBUG_CODE_COMPLETION) {
log("getGlobalCompletions", current, state);
}
List<IToken> completions = new ArrayList<IToken>();
//in completion with nothing, just go for what is imported and global tokens.
for (int i = 0; i < globalTokens.length; i++) {
completions.add(globalTokens[i]);
}
//now go for the token imports
for (int i = 0; i < importedModules.length; i++) {
completions.add(importedModules[i]);
}
if (!state.getBuiltinsGotten()) {
state.setBuiltinsGotten(true);
if (DebugSettings.DEBUG_CODE_COMPLETION) {
Log.toLogFile(this, "getBuiltinCompletions");
}
//last thing: get completions from module __builtin__
getBuiltinCompletions(state, completions);
if (DebugSettings.DEBUG_CODE_COMPLETION) {
Log.toLogFile(this, "END getBuiltinCompletions");
}
}
//wild imports: recursively go and get those completions. Must be done before getting the builtins, because
//when we do a wild import, we may get tokens that are filtered, and there's a chance that the builtins get
//filtered out if they are gotten from a wild import and not from the module itself.
for (int i = 0; i < wildImportedModules.length; i++) {
//for wild imports, we must get the global completions with __all__ filtered
IToken name = wildImportedModules[i];
getCompletionsForWildImport(state, current, completions, name);
}
return completions;
}
/**
* @return the builtin completions
*/
public List<IToken> getBuiltinCompletions(ICompletionState state, List<IToken> completions) {
IPythonNature nature = state.getNature();
IToken[] builtinCompletions = getBuiltinComps(nature);
if (builtinCompletions != null) {
for (int i = 0; i < builtinCompletions.length; i++) {
completions.add(builtinCompletions[i]);
}
}
return completions;
}
/**
* @return the tokens in the builtins
*/
protected IToken[] getBuiltinComps(IPythonNature nature) {
return nature.getBuiltinCompletions();
}
/**
* @return the module that represents the builtins
*/
protected IModule getBuiltinMod(IPythonNature nature) {
return nature.getBuiltinMod();
}
/**
* Resolves a token defined with 'from module import something' statement
* to a proper type, as defined in module.
* @param imported the token to resolve.
* @return the resolved token or the original token in case no additional information could be obtained.
* @throws CompletionRecursionException
*/
public ImmutableTuple<IModule, IToken> resolveImport(ICompletionState state, final IToken imported, IModule current)
throws CompletionRecursionException {
String currModName = imported.getParentPackage();
Tuple3<IModule, String, IToken> modTok = findOnImportedMods(new IToken[] { imported },
state.getCopyWithActTok(imported.getRepresentation()), currModName, current);
if (modTok != null && modTok.o1 != null) {
if (modTok.o2.length() == 0) {
return new ImmutableTuple<IModule, IToken>(current, imported); //it's a module actually, so, no problems...
} else {
try {
state.checkResolveImportMemory(modTok.o1, modTok.o2);
} catch (CompletionRecursionException e) {
return new ImmutableTuple<IModule, IToken>(current, imported);
}
IToken repInModule = getRepInModule(modTok.o1, modTok.o2, state.getNature(), state);
if (repInModule != null) {
return new ImmutableTuple<IModule, IToken>(modTok.o1, repInModule);
}
}
}
return new ImmutableTuple<IModule, IToken>(current, imported);
}
/**
* This is the public interface
* @throws CompletionRecursionException
* @see org.python.pydev.core.ICodeCompletionASTManager#getRepInModule(org.python.pydev.core.IModule, java.lang.String, org.python.pydev.core.IPythonNature)
*/
public IToken getRepInModule(IModule module, String tokName, IPythonNature nature)
throws CompletionRecursionException {
return getRepInModule(module, tokName, nature, null);
}
/**
* Get the actual token representing the tokName in the passed module
* @param module the module where we're looking
* @param tokName the name of the token we're looking for
* @param nature the nature we're looking for
* @return the actual token in the module (or null if it was not possible to find it).
* @throws CompletionRecursionException
*/
private IToken getRepInModule(IModule module, String tokName, IPythonNature nature, ICompletionState state)
throws CompletionRecursionException {
if (module != null) {
if (tokName.startsWith(".")) {
tokName = tokName.substring(1);
}
//ok, we are getting some token from the module... let's see if it is really available.
String[] headAndTail = FullRepIterable.headAndTail(tokName);
String actToken = headAndTail[0]; //tail (if os.path, it is os)
String hasToBeFound = headAndTail[1]; //head (it is path)
//if it was os.path:
//initial would be os.path
//foundAs would be os
//actToken would be path
//now, what we will do is try to do a code completion in os and see if path is found
if (state == null) {
state = CompletionStateFactory.getEmptyCompletionState(actToken, nature, new CompletionCache());
} else {
state = state.getCopy();
state.setActivationToken(actToken);
}
IToken[] completionsForModule = getCompletionsForModule(module, state);
int len = completionsForModule.length;
for (int i = 0; i < len; i++) {
IToken foundTok = completionsForModule[i];
if (foundTok.getRepresentation().equals(hasToBeFound)) {
return foundTok;
}
}
}
return null;
}
/* (non-Javadoc)
* @see ICodeCompletionASTManager#getCompletionsForWildImport(ICompletionState, IModule, List, IToken)
*/
public boolean getCompletionsForWildImport(ICompletionState state, IModule current, List<IToken> completions,
IToken name) {
try {
//this one is an exception... even though we are getting the name as a relative import, we say it
//is not because we want to get the module considering __init__
IModule mod = null;
if (current != null) {
//we cannot get the relative path if we don't have a current module
mod = getModule(name.getAsRelativeImport(current.getName()), state.getNature(), false);
}
if (mod == null) {
mod = getModule(name.getOriginalRep(), state.getNature(), false); //absolute import
}
if (mod != null) {
state.checkWildImportInMemory(current, mod);
IToken[] completionsForModule = getCompletionsForModule(mod, state, true, false, true);
for (IToken token : completionsForModule) {
completions.add(token);
}
return true;
} else {
//"Module not found:" + name.getRepresentation()
}
} catch (CompletionRecursionException e) {
//probably found a recursion... let's return the tokens we have so far
}
return false;
}
public IToken[] findTokensOnImportedMods(IToken[] importedModules, ICompletionState state, IModule current)
throws CompletionRecursionException {
Tuple3<IModule, String, IToken> o = findOnImportedMods(importedModules, state, current.getName(), current);
if (o == null)
return null;
IModule mod = o.o1;
String tok = o.o2;
String tokForSearchInOtherModule = getTokToSearchInOtherModule(o);
if (tok.length() == 0) {
//the activation token corresponds to an imported module. We have to get its global tokens and return them.
ICompletionState copy = state.getCopy();
copy.setActivationToken("");
copy.setBuiltinsGotten(true); //we don't want builtins...
return getCompletionsForModule(mod, copy);
} else if (mod != null) {
ICompletionState copy = state.getCopy();
copy.setActivationToken(tokForSearchInOtherModule);
copy.setCol(-1);
copy.setLine(-1);
copy.raiseNFindTokensOnImportedModsCalled(mod, tokForSearchInOtherModule);
String parentPackage = o.o3.getParentPackage();
if (parentPackage.trim().length() > 0 && parentPackage.equals(current.getName())
&& state.getActivationToken().equals(tok) && !parentPackage.endsWith("__init__")) {
String name = mod.getName();
if (name.endsWith(".__init__")) {
name = name.substring(0, name.length() - 9);
}
if (o.o3.getAsAbsoluteImport().startsWith(name)) {
if (current.isInDirectGlobalTokens(tok, state)) {
return null;
}
}
}
return getCompletionsForModule(mod, copy);
}
return null;
}
/**
* When we have an import, we have one token which we used to find it and another which is the
* one we refer to at the current module. This method will get the way it's referred at the
* actual module and not at the current module (at the current module it's modTok.o2).
*/
public static String getTokToSearchInOtherModule(Tuple3<IModule, String, IToken> modTok) {
String tok = modTok.o2;
String tokForSearchInOtherModule = tok;
if (tok.length() > 0) {
IToken sourceToken = modTok.o3;
if (sourceToken instanceof SourceToken) {
SourceToken sourceToken2 = (SourceToken) sourceToken;
if (sourceToken2.getAst() instanceof ImportFrom) {
ImportFrom importFrom = (ImportFrom) sourceToken2.getAst();
if (importFrom.names.length > 0 && importFrom.names[0].asname != null) {
String originalRep = sourceToken.getOriginalRep();
tokForSearchInOtherModule = FullRepIterable.getLastPart(originalRep);
}
}
}
}
return tokForSearchInOtherModule;
}
/**
* @param activationToken
* @param importedModules
* @param module
* @return tuple with:
* 0: mod
* 1: tok
* @throws CompletionRecursionException
*/
public Tuple3<IModule, String, IToken> findOnImportedMods(ICompletionState state, IModule current)
throws CompletionRecursionException {
IToken[] importedModules = current.getTokenImportedModules();
return findOnImportedMods(importedModules, state, current.getName(), current);
}
/**
* This function tries to find some activation token defined in some imported module.
* @return tuple with: the module and the token that should be used from it.
*
* @param this is the activation token we have. It may be a single token or some dotted name.
*
* If it is a dotted name, such as testcase.TestCase, we need to match against some import
* represented as testcase or testcase.TestCase.
*
* If a testcase.TestCase matches against some import named testcase, the import is returned and
* the TestCase is put as the module
*
* 0: mod
* 1: tok (string)
* 2: actual tok
* @throws CompletionRecursionException
*/
public Tuple3<IModule, String, IToken> findOnImportedMods(IToken[] importedModules, ICompletionState state,
String currentModuleName, IModule current) throws CompletionRecursionException {
FullRepIterable iterable = new FullRepIterable(state.getActivationToken(), true);
for (String tok : iterable) {
for (IToken importedModule : importedModules) {
final String modRep = importedModule.getRepresentation(); //this is its 'real' representation (alias) on the file (if it is from xxx import a as yyy, it is yyy)
if (modRep.equals(tok)) {
String act = state.getActivationToken();
Tuple<IModule, String> r;
try {
r = findOnImportedMods(importedModule, tok, state, act, currentModuleName, current);
if (r != null) {
return new Tuple3<IModule, String, IToken>(r.o1, r.o2, importedModule);
}
//Note, if r==null, even though the name matched, keep on going (to handle cases of
//try..except ImportError, as we cannot be sure of which version will actually match).
} catch (MisconfigurationException e) {
Log.log(e);
}
}
}
}
return null;
}
public Tuple<IModule, String> findModule(String moduleToFind, String currentModule, ICompletionState state,
IModule current) throws CompletionRecursionException, MisconfigurationException {
NameTok name = new NameTok(moduleToFind, NameTok.ImportModule);
Import impTok = new Import(new aliasType[] { new aliasType(name, null) });
List<IToken> tokens = new ArrayList<IToken>();
List<IToken> imp = AbstractVisitor.makeImportToken(impTok, tokens, currentModule, true);
IToken importedModule = imp.get(imp.size() - 1); //get the last one (it's the one with the 'longest' representation).
return this.findOnImportedMods(importedModule, "", state, "", currentModule, current);
}
/**
* Checks if some module can be resolved and returns the module it is resolved to (and to which token).
* @throws CompletionRecursionException
* @throws MisconfigurationException
*
*/
public Tuple<IModule, String> findOnImportedMods(IToken importedModule, String tok, ICompletionState state,
String activationToken, String currentModuleName, IModule current) throws CompletionRecursionException,
MisconfigurationException {
Tuple<IModule, String> modTok = null;
IModule mod = null;
//ok, check if it is a token for the new import
IPythonNature nature = state.getNature();
if (importedModule instanceof SourceToken) {
SourceToken token = (SourceToken) importedModule;
if (token.isImportFrom()) {
ImportFrom importFrom = (ImportFrom) token.getAst();
int level = importFrom.level;
if (level > 0) {
//ok, it must be treated as a relative import
//ok, it is the import added on python 2.5 (from .. import xxx)
String parentPackage = token.getParentPackage();
List<String> moduleParts = StringUtils.dotSplit(parentPackage);
String relative = null;
if (moduleParts.size() > level) {
relative = FullRepIterable.joinParts(moduleParts, moduleParts.size() - level);
}
String modName = ((NameTok) importFrom.module).id;
if (modName.length() > 0) {
//ok, we have to add the other part too, as we have more than the leading dots
//from ..bar import
relative += "." + modName;
}
if (!AbstractVisitor.isWildImport(importFrom)) {
tok = FullRepIterable.getLastPart(token.originalRep);
relative += "." + tok;
}
modTok = findModuleFromPath(relative, nature, false, null);
mod = modTok.o1;
if (checkValidity(currentModuleName, mod)) {
Tuple<IModule, String> ret = fixTok(modTok, tok, activationToken);
return ret;
}
//ok, it is 'forced' as relative import because it has a level, so, it MUST return here
return null;
}
}
}
boolean isAbsoluteImportEnabledx = this.isAbsoluteImportEnabled(current, nature);
String asRelativeImport = "";
if (!isAbsoluteImportEnabledx) {
//check as relative with complete rep
asRelativeImport = importedModule.getAsRelativeImport(currentModuleName);
if (!asRelativeImport.startsWith(".")) {
modTok = findModuleFromPath(asRelativeImport, nature, true, currentModuleName);
mod = modTok.o1;
if (checkValidity(currentModuleName, mod)) {
Tuple<IModule, String> ret = fixTok(modTok, tok, activationToken);
return ret;
}
}
}
//check if the import actually represents some token in an __init__ file
String originalWithoutRep = importedModule.getOriginalWithoutRep();
if (originalWithoutRep.length() > 0) {
if (!originalWithoutRep.endsWith("__init__")) {
originalWithoutRep = originalWithoutRep + ".__init__";
}
modTok = findModuleFromPath(originalWithoutRep, nature, true, null);
mod = modTok.o1;
if (modTok.o2.endsWith("__init__") == false && checkValidity(currentModuleName, mod)) {
if (mod.isInGlobalTokens(importedModule.getRepresentation(), nature, false, state)) {
//then this is the token we're looking for (otherwise, it might be a module).
Tuple<IModule, String> ret = fixTok(modTok, tok, activationToken);
if (ret.o2.length() == 0) {
ret.o2 = importedModule.getRepresentation();
} else {
ret.o2 = importedModule.getRepresentation() + "." + ret.o2;
}
return ret;
}
}
}
//the most 'simple' case: check as absolute with original rep
modTok = findModuleFromPath(importedModule.getOriginalRep(), nature, false, null);
mod = modTok.o1;
if (checkValidity(currentModuleName, mod)) {
Tuple<IModule, String> ret = fixTok(modTok, tok, activationToken);
return ret;
}
if (!isAbsoluteImportEnabledx) {
//ok, one last shot, to see a relative looking in folders __init__
modTok = findModuleFromPath(asRelativeImport, nature, false, null);
mod = modTok.o1;
if (checkValidity(currentModuleName, mod, true)) {
Tuple<IModule, String> ret = fixTok(modTok, tok, activationToken);
//now let's see if what we did when we found it as a relative import is correct:
//if we didn't find it in an __init__ module, all should be ok
if (!mod.getName().endsWith("__init__")) {
return ret;
}
//otherwise, we have to be more cautious...
//if the activation token is empty, then it is the module we were looking for
//if it is not the initial token we were looking for, it is correct
//if it is in the global tokens of the found module it is correct
//if none of this situations was found, we probably just found the same token we had when we started (unless I'm mistaken...)
else if (activationToken.length() == 0 || ret.o2.equals(activationToken) == false
|| mod.isInGlobalTokens(activationToken, nature, false, state)) {
return ret;
}
}
}
return null;
}
protected boolean checkValidity(String currentModuleName, IModule mod) {
return checkValidity(currentModuleName, mod, false);
}
/**
* @param isRelative: On a relative import we have to check some more conditions...
*/
protected boolean checkValidity(String currentModuleName, IModule mod, boolean isRelative) {
if (mod == null) {
return false;
}
String modName = mod.getName();
if (modName == null) {
return true;
}
//still in the same module
if (modName.equals(currentModuleName)) {
return false;
}
if (isRelative && currentModuleName != null && modName.endsWith(".__init__")) {
//we have to check it without the __init__
//what happens here is that considering the structure:
//
// xxx.__init__
// xxx.mod1
//
// we cannot have tokens from the mod1 getting __init__
String withoutLastPart = FullRepIterable.getWithoutLastPart(modName);
String currentWithoutLastPart = FullRepIterable.getWithoutLastPart(currentModuleName);
if (currentWithoutLastPart.equals(withoutLastPart)) {
return false;
}
}
return true;
}
/**
* Fixes the token if we found a module that was just a substring from the initial activation token.
*
* This means that if we had testcase.TestCase and found it as TestCase, the token is added with TestCase
*/
protected Tuple<IModule, String> fixTok(Tuple<IModule, String> modTok, String tok, String activationToken) {
if (activationToken.length() > tok.length() && activationToken.startsWith(tok)) {
String toAdd = activationToken.substring(tok.length() + 1);
if (modTok.o2.length() == 0) {
modTok.o2 = toAdd;
} else {
modTok.o2 += "." + toAdd;
}
}
return modTok;
}
/**
* This function receives a path (rep) and extracts a module from that path.
* First it tries with the full path, and them removes a part of the final of
* that path until it finds the module or the path is empty.
*
* @param currentModuleName this is the module name (used to check validity for relative imports) -- not used if dontSearchInit is false
* if this parameter is not null, it means we're looking for a relative import. When checking for relative imports,
* we should only check the modules that are directly under this project (so, we should not check the whole pythonpath for
* it, just direct modules)
*
* @return tuple with found module and the String removed from the path in
* order to find the module.
*/
public Tuple<IModule, String> findModuleFromPath(String rep, IPythonNature nature, boolean dontSearchInit,
String currentModuleName) {
String tok = "";
boolean lookingForRelative = currentModuleName != null;
IModule mod = getModule(rep, nature, dontSearchInit, lookingForRelative);
String mRep = rep;
int index;
while (mod == null && (index = mRep.lastIndexOf('.')) != -1) {
tok = mRep.substring(index + 1) + "." + tok;
mRep = mRep.substring(0, index);
if (mRep.length() > 0) {
mod = getModule(mRep, nature, dontSearchInit, lookingForRelative);
}
}
if (tok.endsWith(".")) {
tok = tok.substring(0, tok.length() - 1); //remove last point if found.
}
if (dontSearchInit && currentModuleName != null && mod != null) {
String parentModule = FullRepIterable.getParentModule(currentModuleName);
//if we are looking for some relative import token, it can only match if the name found is not less than the parent
//of the current module because of the way in that relative imports are meant to be written.
//if it equal, it should not match either, as it was found as the parent module... this can not happen because it must find
//it with __init__ if it was the parent module
if (mod.getName().length() <= parentModule.length()) {
return new Tuple<IModule, String>(null, null);
}
}
return new Tuple<IModule, String>((AbstractModule) mod, tok);
}
}